# -*- coding: utf-8 -*-
"""
浏览器配置和启动管理
从conftest.py中提取的配置，可以直接在Playwright中使用
"""

import os
import time
import json
from pathlib import Path
from typing import Dict, Any, Optional
from playwright.sync_api import sync_playwright, Browser, BrowserContext, Page
from Common.handle_log import do_log
from Common.handle_path import AUTH_DIR


class BrowserManager:
    """浏览器管理器，封装浏览器启动配置和会话管理"""
    
    def __init__(self, storage_dir: Optional[str] = None):
        """
        初始化浏览器管理器
        :param storage_dir: 存储会话状态的目录，默认为项目根目录下的auth文件夹
        """
        if storage_dir is None:
            self.storage_dir = Path(AUTH_DIR)
        else:
            self.storage_dir = Path(storage_dir)

        self.storage_path = Path(self.storage_dir) / "storage_state.json"
        self.cert_version_path = Path(self.storage_dir) / "certificate_version.json"
        # self.login_url = "https://www.iqiyi.com/iv#/personal?ver=tesla"
        self.login_url = "https://www.iqiyi.com/iv#/?ver=tesla"
        self.logger = do_log
        
        # 轻量"隐身"脚本，降低站点对自动化环境的拦截概率
        self.stealth_js = r"""
        // 隐藏 webdriver 标记
        Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
        // 伪造语言列表
        Object.defineProperty(navigator, 'languages', { get: () => ['zh-CN', 'zh'] });
        // 伪造插件数量
        Object.defineProperty(navigator, 'plugins', { get: () => [1,2,3,4,5] });
        // 补齐 window.chrome
        if (!window.chrome) { window.chrome = { runtime: {} }; }
        """
    
    def get_browser_launch_args(self) -> Dict[str, Any]:
        """
        获取浏览器启动参数
        :return: 浏览器启动参数字典
        """
        return {
            "headless": False,
            # 使用系统安装的 Google Chrome（包含 Widevine CDM 与更多媒体编解码器）
            "channel": "chrome",
            # 取消 Playwright 默认静音，并放宽自动播放策略，提升首帧可播放概率
            "ignore_default_args": [
                "--mute-audio",
            ],
            "args": [
                "--autoplay-policy=no-user-gesture-required",
            ],
        }
    
    def get_browser_context_args(self) -> Dict[str, Any]:
        """
        获取浏览器上下文参数
        :return: 浏览器上下文参数字典
        """
        args = {
            # 统一语言/时区/UA，提升与手动 Chrome 一致性
            "locale": "zh-CN",
            "timezone_id": "Asia/Shanghai",
            "user_agent": (
                "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
                "AppleWebKit/537.36 (KHTML, like Gecko) "
                "Chrome/126.0.0.0 Safari/537.36"
            ),
            # 设置页面可视区域大小（不包括浏览器功能区）
            "viewport": {"width": 1180, "height": 820},
            # 仅模拟渲染比例（DPR），不切换为移动端
            "device_scale_factor": 2,
            "is_mobile": False,
            "has_touch": False,
        }
        
        # 如果存在已保存的登录状态，则加载
        if self.storage_path.exists():
            args["storage_state"] = str(self.storage_path)
            self.logger.info(f"加载已保存的登录状态: {self.storage_path}")
        
        return args
    
    def create_storage_state(self, browser: Browser, login_url: Optional[str] = None) -> bool:
        """
        在首次运行时引导手动登录并保存会话文件
        :param browser: 浏览器实例
        :param login_url: 登录页面URL，不指定则使用默认值
        :return: 是否成功创建登录状态
        """
        try:
            self.storage_dir.mkdir(parents=True, exist_ok=True)
            
            # 使用基本配置创建上下文（不加载登录状态）
            basic_args = {
                "locale": "zh-CN",
                "timezone_id": "Asia/Shanghai",
                "user_agent": (
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
                    "AppleWebKit/537.36 (KHTML, like Gecko) "
                    "Chrome/126.0.0.0 Safari/537.36"
                ),
                "viewport": {"width": 1180, "height": 820},
                "device_scale_factor": 2,
                "is_mobile": False,
                "has_touch": False,
            }
            
            context = browser.new_context(**basic_args)
            context.add_init_script(self.stealth_js)
            
            page = context.new_page()
            page.goto(login_url or self.login_url)
            
            print(f"\n请在已打开的浏览器窗口中完成登录（如扫码/输入账号）。")
            print(f"将在 60 秒后自动保存会话信息并关闭。\n")
            
            # 等待用户登录
            time.sleep(60)
            
            # 保存登录状态
            context.storage_state(path=str(self.storage_path))

            # 读取并保存隐私弹窗同意状态 certificateVersion
            try:
                cert_value = None
                cert_source = None

                # 优先从 localStorage 获取
                try:
                    cert_value = page.evaluate("() => window.localStorage.getItem('certificateVersion')")
                    if cert_value:
                        cert_source = "localStorage"
                except Exception as _:
                    pass

                # 若 localStorage 未取到，则尝试从 Cookie 获取
                if not cert_value:
                    try:
                        cookies = context.cookies()
                        for cookie in cookies:
                            if cookie.get("name") == "certificateVersion":
                                cert_value = cookie.get("value")
                                cert_source = "cookie"
                                break
                    except Exception as _:
                        pass

                data = {
                    "certificateVersion": cert_value,
                    "source": cert_source,
                    "pageUrl": page.url,
                    "savedAt": int(time.time()),
                }
                # 确保目录存在
                self.storage_dir.mkdir(parents=True, exist_ok=True)
                with open(self.cert_version_path, "w", encoding="utf-8") as f:
                    json.dump(data, f, ensure_ascii=False, indent=2)
                self.logger.info(f"已保存 certificateVersion 到: {self.cert_version_path}，来源: {cert_source}")
            except Exception as e:
                self.logger.warning(f"保存 certificateVersion 失败: {str(e)}")
            context.close()
            
            self.logger.info(f"成功保存登录状态到: {self.storage_path}")
            return True
            
        except Exception as e:
            self.logger.error(f"创建登录状态失败: {str(e)}")
            return False
    
    def ensure_login_state(self, browser: Browser, login_url: Optional[str] = None) -> bool:
        """
        确保存在登录状态，不存在则引导创建
        :param browser: 浏览器实例
        :param login_url: 登录页面URL
        :return: 是否确保成功
        """
        if not self.storage_path.exists():
            self.logger.info("未找到登录状态文件，开始引导登录...")
            return self.create_storage_state(browser, login_url)
        else:
            self.logger.info(f"已找到登录状态文件: {self.storage_path}")
            return True
    
    def create_browser_context(self, browser: Browser, auto_login: bool = True, 
                             login_url: Optional[str] = None) -> BrowserContext:
        """
        创建配置好的浏览器上下文
        :param browser: 浏览器实例
        :param auto_login: 是否自动处理登录状态
        :param login_url: 登录页面URL
        :return: 浏览器上下文
        """
        if auto_login:
            self.ensure_login_state(browser, login_url)
        
        context_args = self.get_browser_context_args()
        context = browser.new_context(**context_args)
        
        # 注入隐身脚本
        try:
            context.add_init_script(self.stealth_js)
            self.logger.info("成功注入隐身脚本")
        except Exception as e:
            self.logger.warning(f"注入隐身脚本失败: {str(e)}")
        
        return context
    
    def create_page_with_logging(self, context: BrowserContext) -> Page:
        """
        创建带有日志监听的页面
        :param context: 浏览器上下文
        :return: 页面实例
        """
        page = context.new_page()
        
        # 添加页面事件监听
        # page.on("console", lambda m: self.logger.info(f"PAGE LOG: {m.type} {m.text}"))
        # page.on("pageerror", lambda e: self.logger.error(f"PAGE ERROR: {e}"))
        # page.on("requestfailed", lambda r: self.logger.warning(f"REQUEST FAILED: {r.url} {r.failure}"))
        
        return page


class EnhancedPlaywright:
    """
    增强版Playwright，封装了浏览器管理功能
    """
    
    def __init__(self, storage_dir: Optional[str] = None):
        """
        初始化增强版Playwright
        :param storage_dir: 存储目录
        """
        self.browser_manager = BrowserManager(storage_dir)
        self.playwright = None
        self.browser = None
        self.context = None
        self.page = None
    
    def __enter__(self):
        """上下文管理器入口"""
        self.playwright = sync_playwright().start()
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        """上下文管理器出口"""
        if self.page:
            self.page.close()
        if self.context:
            self.context.close()
        if self.browser:
            self.browser.close()
        if self.playwright:
            self.playwright.stop()
    
    def launch_browser(self, browser_type: str = "chromium", **kwargs) -> Browser:
        """
        启动浏览器
        :param browser_type: 浏览器类型 ('chromium', 'firefox', 'webkit')
        :param kwargs: 额外的启动参数
        :return: 浏览器实例
        """
        if not self.playwright:
            raise RuntimeError("必须在with语句中使用EnhancedPlaywright")
        
        # 获取默认启动参数
        launch_args = self.browser_manager.get_browser_launch_args()
        launch_args.update(kwargs)
        
        # 选择浏览器类型
        browser_launcher = getattr(self.playwright, browser_type)
        self.browser = browser_launcher.launch(**launch_args)
        
        return self.browser
    
    def create_context(self, auto_login: bool = True, login_url: Optional[str] = None, **kwargs) -> BrowserContext:
        """
        创建浏览器上下文
        :param auto_login: 是否自动处理登录
        :param login_url: 登录页面URL
        :param kwargs: 额外的上下文参数
        :return: 浏览器上下文
        """
        if not self.browser:
            raise RuntimeError("必须先调用launch_browser()")
        
        self.context = self.browser_manager.create_browser_context(
            self.browser, auto_login, login_url
        )
        
        return self.context
    
    def create_page(self, with_logging: bool = True) -> Page:
        """
        创建页面
        :param with_logging: 是否启用日志监听
        :return: 页面实例
        """
        if not self.context:
            raise RuntimeError("必须先调用create_context()")
        
        if with_logging:
            self.page = self.browser_manager.create_page_with_logging(self.context)
        else:
            self.page = self.context.new_page()
        
        return self.page
    
    def get_configured_setup(self, auto_login: bool = True, login_url: Optional[str] = None, 
                           browser_type: str = "chromium") -> tuple[Browser, BrowserContext, Page]:
        """
        一站式获取配置好的浏览器、上下文和页面
        :param auto_login: 是否自动处理登录
        :param login_url: 登录页面URL
        :param browser_type: 浏览器类型
        :return: (浏览器, 上下文, 页面) 元组
        """
        browser = self.launch_browser(browser_type)
        context = self.create_context(auto_login, login_url)
        page = self.create_page()
        
        return browser, context, page


# 便捷函数
def create_configured_browser():
    """
    创建配置好的浏览器实例（简化版）
    :return: EnhancedPlaywright实例
    """
    return EnhancedPlaywright()


def quick_start(auto_login: bool = False, login_url: Optional[str] = None):
    """
    快速启动配置好的浏览器
    :param auto_login: 是否自动处理登录
    :param login_url: 登录页面URL
    :return: (playwright_instance, browser, context, page)
    """
    pw = EnhancedPlaywright()
    pw.__enter__()
    
    browser, context, page = pw.get_configured_setup(auto_login, login_url)
    
    return pw, browser, context, page
